/*
 * Toedo
 * Copyright (C) 2009-2010 J. Devauchelle and contributors.
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * version 3 as published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

package org.toedo.controllers;

import java.beans.PropertyChangeListener;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringWriter;

import org.eclipse.mylyn.wikitext.confluence.core.ConfluenceLanguage;
import org.eclipse.mylyn.wikitext.core.parser.MarkupParser;
import org.eclipse.mylyn.wikitext.core.parser.builder.HtmlDocumentBuilder;
import org.eclipse.mylyn.wikitext.core.parser.markup.MarkupLanguage;
import org.eclipse.mylyn.wikitext.mediawiki.core.MediaWikiLanguage;
import org.eclipse.mylyn.wikitext.textile.core.TextileLanguage;
import org.eclipse.mylyn.wikitext.tracwiki.core.TracWikiLanguage;
import org.toedo.controllers.events.EventConstants;
import org.toedo.controllers.events.EventController;
import org.toedo.logging.Log;
import org.toedo.model.export.HtmlGenerator;

public class ParserController implements PropertyChangeListener {
	
	public static final String DEFAULT_DISPLAY_CSS_PATH = "org/toedo/resources/css/default-display.css"; //$NON-NLS-1$
	
	private static final String XHTML_DOCTYPE = "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\" \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">\n"; //$NON-NLS-1$
	private static final String XHTML_HTML_OPEN = "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\" lang=\"en\">\n"; //$NON-NLS-1$
	private static final String XHTML_HTML_CLOSE = "</html>\n"; //$NON-NLS-1$
	private static final String XHTML_HEAD_OPEN = "<head>\n"; //$NON-NLS-1$
	private static final String XHTML_HEAD_CLOSE = "</head>\n"; //$NON-NLS-1$
	private static final String XHTML_META = "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=UTF-8\" />\n"; //$NON-NLS-1$
	private static final String XHTML_BODY_OPEN = "<body>\n"; //$NON-NLS-1$
	private static final String XHTML_BODY_CLOSE = "</body>\n"; //$NON-NLS-1$
	
	private static final String XHTML_STYLE_OPEN = "<style type=\"text/css\" media=\"screen\">\n"; //$NON-NLS-1$
	private static final String XHTML_STYLE_CLOSE = "</style>\n"; //$NON-NLS-1$
	
	private String mHtmlHeader = null;
	private String mHtmlFooter = null;
	
	private MarkupParser mMarkupParser = null;
	
	/**
	 * Holder for singleton implementation.
	 */
	private static class HtmlUtilsHolder {
		private static final ParserController INSTANCE = new ParserController();
	}
	
	/**
	 * Get the unique instance of the Controller.
	 * @return The instance of the Controller
	 */
	public static ParserController getInstance() {
		return HtmlUtilsHolder.INSTANCE;
	}
	
	private ParserController() {
		EventController.getInstance().addGeneralEventsListener(this);
	}
	
	public String getDefaultDisplayCss() {
		StringBuilder sb = new StringBuilder();
		
		InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream(DEFAULT_DISPLAY_CSS_PATH);
		if (stream != null) {

			try {
				InputStreamReader streamReader = new InputStreamReader(stream);
				BufferedReader buffer = new BufferedReader(streamReader);						

				String line = null;
				while ( null != (line = buffer.readLine())){
					sb.append(line + "\n"); //$NON-NLS-1$
				}
			} catch (IOException e) {
				Log.getInstance(HtmlGenerator.class).warn("Unable to include default css."); //$NON-NLS-1$
			}
		}
		
		return sb.toString();
	}
	
	private String getCss() {
		StringBuilder sb = new StringBuilder();
		
		String css = DataController.getInstance().getProject().getDisplayCss();
		if ((css == null) ||
				(css.isEmpty())) {
			css = getDefaultDisplayCss();
		}
		
		sb.append(XHTML_STYLE_OPEN);
		sb.append(css);		
		sb.append(XHTML_STYLE_CLOSE);
		
		return sb.toString();
	}
	
	private MarkupLanguage getMarkupLanguage() {
		switch (DataController.getInstance().getProject().getMarkupType()) {
		case EnTextile : return new TextileLanguage();
		case EnMediawiki : return new MediaWikiLanguage();
		case EnTrac : return new TracWikiLanguage();
		case EnConfluence : return new ConfluenceLanguage();
		default : return new TextileLanguage();
		}
	}
	
	private void initializeParser() {				
		mMarkupParser = new MarkupParser();
		mMarkupParser.setMarkupLanguage(getMarkupLanguage());
	}
	
	private void buildHtmlHeader() {
		StringBuilder sb = new StringBuilder();
		
		sb.append(XHTML_DOCTYPE);
		sb.append(XHTML_HTML_OPEN);
		sb.append(XHTML_HEAD_OPEN);
		sb.append(XHTML_META);

		sb.append(getCss());
			
		sb.append(XHTML_HEAD_CLOSE);
		sb.append(XHTML_BODY_OPEN);
		
		mHtmlHeader = sb.toString();
	}
	
	private void buildHtmlFooter() {
		StringBuilder sb = new StringBuilder();
		
		sb.append(XHTML_BODY_CLOSE);
		sb.append(XHTML_HTML_CLOSE);
		
		mHtmlFooter = sb.toString();
	}
	
	private String getHtmlHeader() {
		if (mHtmlHeader == null) {
			buildHtmlHeader();
		}
		return mHtmlHeader;
	}
	
	private String getHtmlFooter() {
		if (mHtmlFooter == null) {
			buildHtmlFooter();
		}
		return mHtmlFooter;
	}
	
	public String getHtml(String input) {
		if (mMarkupParser == null) {
			initializeParser();
		}
		
		StringWriter htmlWriter = new StringWriter();
		HtmlDocumentBuilder builder = new HtmlDocumentBuilder(htmlWriter);
		builder.setEmitAsDocument(false);
		mMarkupParser.setBuilder(builder);
		
		mMarkupParser.parse(input);
		
		StringBuilder sb = new StringBuilder();
		sb.append(getHtmlHeader());
		sb.append(htmlWriter.toString());
		sb.append(getHtmlFooter());
		
		return sb.toString();
	}

	public void dispose() {
		EventController.getInstance().removeGeneralEventsListener(this);
	}
	
	@Override
	public void propertyChange(java.beans.PropertyChangeEvent arg0) {
		if (arg0.getPropertyName().equals(EventConstants.EVT_PROJECT_PROPERTIES_CHANGE)) {
			if (((String) arg0.getNewValue()).equals(EventConstants.EVT_PROJECT_PROPERTIES_DISPLAY_CSS_CHANGE)) {
				// Reset mHtmlHeader in order to force reload of display css.
				mHtmlHeader = null;
			} else if (((String) arg0.getNewValue()).equals(EventConstants.EVT_PROJECT_PROPERTIES_MARKUP_CHANGE)) {
				// Reset parser.
				mMarkupParser = null;
			}
		}
					
	}

}
